package com.syspeak.modules.utils.json;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.json.JSONException;
import org.apache.struts2.json.JSONUtil;
import org.apache.struts2.json.SerializationParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.syspeak.modules.spring.web.json.JsonConfigedObject;
import com.syspeak.modules.spring.web.servlet.mvc.annotation.JsonConfig;

/**
 * 基于JSON-LIB的ObjectMapper工具类
 * @company SysPeak (C) Copyright
 * @category JsonLibObjectMapper
 * @version 1.0
 * @since 2011-10-25
 * @author JemiZhuu(周士淳)
 */
public class JsonLibObjectMapper {

	private final Logger logger = LoggerFactory.getLogger(getClass());
	private String defaultEncoding = "UTF-8";

	public void serialize(Writer writer, Object object, String excludeProperties, String includeProperties,
			boolean excludeNullProperties) throws IOException, JSONException {
		JSONUtil.serialize(writer, object, this.getExcludeProperties(excludeProperties),
				this.getIncludeProperties(includeProperties), excludeNullProperties);
	}

	public void serialize(OutputStream out, Object object, String excludeProperties, String includeProperties,
			boolean excludeNullProperties) throws IOException, JSONException {
		Writer writer = new OutputStreamWriter(out);
		this.serialize(writer, object, excludeProperties, includeProperties, excludeNullProperties);
	}

	public void writeValueToResponse(Object value, HttpServletResponse response, boolean writeGzip) throws Exception {
		Assert.notNull(value, "write value to json can`t null");
		String json = null;
		boolean ignoreHierarchy = true;
		boolean enumAsBean = false;
		boolean wrapWithComments = false;
		boolean prefix = false;
		boolean noCache = false;
		String contentType = null;
		String wrapPrefix = null;
		String wrapSuffix = null;
		int statusCode = -1;
		int errorCode = -1;
		if (JsonConfigedObject.class.isAssignableFrom(value.getClass())) {
			JsonConfig jsonConfig = ((JsonConfigedObject) value).getJsonConfig();
			if (jsonConfig != null) {
				String includeProperties = jsonConfig.includeProperties();
				String excludeProperties = jsonConfig.excludeProperties();
				boolean excludeNullProperties = jsonConfig.excludeNullProperties();
				statusCode = jsonConfig.statusCode();
				errorCode = jsonConfig.errorCode();
				json = JSONUtil.serialize((Object) value, this.getExcludeProperties(excludeProperties),
						this.getIncludeProperties(includeProperties), ignoreHierarchy, enumAsBean,
						excludeNullProperties);
			}
		} else {
			json = JSONUtil.serialize(value);
		}
		if (json == null) {
			return;
		}
		try {
			JSONUtil.writeJSONToResponse(new SerializationParams(response, getEncoding(), wrapWithComments, json,
					false, writeGzip, noCache, statusCode, errorCode, prefix, contentType, wrapPrefix, wrapSuffix));
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	/**
	 * @param includedProperties
	 *            the includeProperties to set
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public List<Pattern> getIncludeProperties(String commaDelim) {
		Set<String> includePatterns = JSONUtil.asSet(commaDelim);
		if (includePatterns != null) {
			List<Pattern> includeProperties = new ArrayList<Pattern>(includePatterns.size());

			HashMap existingPatterns = new HashMap();

			for (String pattern : includePatterns) {
				// Compile a pattern for each *unique* "level" of the object
				// hierarchy specified in the regex.
				String[] patternPieces = pattern.split("\\\\\\.");

				String patternExpr = "";
				for (String patternPiece : patternPieces) {
					if (patternExpr.length() > 0) {
						patternExpr += "\\.";
					}
					patternExpr += patternPiece;

					// Check for duplicate patterns so that there is no overlap.
					if (!existingPatterns.containsKey(patternExpr)) {
						existingPatterns.put(patternExpr, patternExpr);

						// Add a pattern that does not have the indexed property
						// matching (ie. list\[\d+\] becomes list).
						if (patternPiece.endsWith("\\]")) {
							includeProperties.add(Pattern.compile(patternExpr.substring(0,
									patternPiece.lastIndexOf("\\["))));

							logger.debug("Adding include property expression:  "
									+ patternExpr.substring(0, patternPiece.lastIndexOf("\\[")));
						}

						includeProperties.add(Pattern.compile(patternExpr));

						logger.debug("Adding include property expression:  " + patternExpr);

						return includeProperties;
					}
				}
			}
		}

		return null;
	}

	public List<Pattern> getExcludeProperties(String commaDelim) {
		Set<String> excludePatterns = JSONUtil.asSet(commaDelim);
		if (excludePatterns != null) {
			List<Pattern> excludeProperties = new ArrayList<Pattern>(excludePatterns.size());
			for (String pattern : excludePatterns) {
				excludeProperties.add(Pattern.compile(pattern));
			}
			return excludeProperties;
		}
		return null;
	}

	protected String getEncoding() {
		String encoding = this.defaultEncoding;

		if (encoding == null) {
			encoding = System.getProperty("file.encoding");
		}

		if (encoding == null) {
			encoding = "UTF-8";
		}

		return encoding;
	}
}
